﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Модуль для клиент-серверного вызова
// Модуль предназначен для работы с бинарными данными ASN и PKCS: 
// низкоуровневый: разбор сообщений ASN, реестр OID
// высокоуровневый: реестр данных о сертификатах найденных в зашифрованном сообщении, 
// сведения о сертификате, данные о подписантах в электронной подписи 
//  
////////////////////////////////////////////////////////////////////////////////

#Область ПрограммныйИнтерфейс

#Область ДляВызоваИзДругихПодсистем

// Следующие процедуры и функции предназначены для интеграции с 1С-Отчетность

// Функция разбирает двоичные данные по формату ASN и раскладывает результат в структуру
//
// Параметры:
//  ДанныеASN 			- ДвоичныеДанные - данные файла, которые надо разобрать как ASN структуру
//  ПараметрыОперации	- Структура - проверяется наличие значений влияющие на режим разбора данных:
//    * ИсключитьСодержимое 		- Булево - исключает избыточный разбор бинарного содержимого, по умолчанию Истина
//    * ОтображатьОшибки 			- Булево - отображает исключение во время разбора, по умолчанию Ложь
//    * ОграничениеDER 			- Булево - отключает анализ байтовых последовательностей
//			
// Возвращаемое значение:
//  - Неопределено
//  - Структура из КлючИЗначение- содержит разобранную структуру в "сыром" виде:
//    * Ключ			- Строка
//    * Значение		- Структура: 
//      ** Тип		- Строка
//      ** Значение	- Произвольный
//      ** Данные	- Строка - представление данных в HEX 
// 
Функция ЧтениеСтруктурыASNФайла(ДанныеASN, ПараметрыОперации = Неопределено) Экспорт
	
	ОпределениеASN	= Новый Структура();
	ОпределениеASN.Вставить("Классы", ОпределитьТип_Классы());
	ОпределениеASN.Вставить("Теги", ОпределитьТип_Теги());
	ОпределениеASN.Вставить("Типы", ОпределитьТип_Тип());
	ОпределениеASN.Вставить("ИсключитьСодержимое", СвойствоСтруктуры(ПараметрыОперации, "ИсключитьСодержимое", Истина));
	ОпределениеASN.Вставить("ОтображатьОшибки", СвойствоСтруктуры(ПараметрыОперации, "ОтображатьОшибки", Ложь));
	ОпределениеASN.Вставить("ОграничениеDER", СвойствоСтруктуры(ПараметрыОперации, "ОграничениеDER", Ложь));
	ОпределениеASN.Вставить("ИзвлечьСОшибками", СвойствоСтруктуры(ПараметрыОперации, "ИзвлечьСОшибками", Истина));
	
	Результат = Неопределено;
	Попытка
		ПотокЧтенияФайла= ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ДанныеASN);
		Результат 		= НоваяСтрока("Начало", Новый Массив);
		ПрочитатьОчереднуюПорцию(ОпределениеASN, ПотокЧтенияФайла, 1, Результат);
	Исключение
		ОтработатьВозникновениеОшибки(ОпределениеASN, ИнформацияОбОшибке());
		Если НЕ ОпределениеASN.ИзвлечьСОшибками Тогда
			Результат = Неопределено;
		КонецЕсли;
	КонецПопытки;	
	
	Возврат Результат;
	
КонецФункции

// Получает представление двоичных данных по указанному адресу
//
// Параметры:
//  ДанныеASN 				- ДвоичныеДанные
//  АдресИзвлечения 		- Строка - в формате ПервыйБайт:ВсегоБайтов
//  ИсключитьПервыеБайты 	- Число - количество первых байтов, которые нужно исключить из представления
//  ТипРезультата			- Строка - поддерживает следующие варианты "HEX", "ДвоичныеДанные", "Base64"
//  ВключитьЗаголовок		- Булево - вернуть данные с учетом заголовка
//
// Возвращаемое значение:
//  Строка
// 
Функция ИзвлечьДанныеСообщения(ДанныеASN, АдресИзвлечения, Знач ИсключитьПервыеБайты = 0, ТипРезультата = "HEX", ВключитьЗаголовок = Ложь) Экспорт
	
	Результат = "";
	РазделительПолей = ":";
	ПотокЧтенияФайла = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ДанныеASN);
	СоставАдреса = СтрРазделить(АдресИзвлечения, РазделительПолей);
	
	Если СоставАдреса.Количество() <> 3 Тогда
		Возврат Результат
	КонецЕсли;
	
	Начало = Число(СоставАдреса[0]) - ?(ВключитьЗаголовок, Число(СоставАдреса[2]), 0) + ИсключитьПервыеБайты;
	Всего = Число(СоставАдреса[1]) + ?(ВключитьЗаголовок, Число(СоставАдреса[2]), 0) - ИсключитьПервыеБайты;
	ВсегоИзвлечь = Начало + Всего;
	
	Если ПотокЧтенияФайла.Размер >= ВсегоИзвлечь Тогда
		СрезДанных = ПотокЧтенияФайла.Прочитать(Начало, Всего);
		Если ТипРезультата = "HEX" Тогда
			Результат = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(СрезДанных);
		ИначеЕсли ТипРезультата = "ДвоичныеДанные" Тогда
			Результат = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(СрезДанных);
		Иначе
			Результат = ПолучитьBase64СтрокуИзБуфераДвоичныхДанных(СрезДанных);
		КонецЕсли;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Функция для формирования массива поддерживаемых объектов OID.
// Также может служить, для анализа полей владельца существующего сертификата, подписи, шифрованного сообщения и прочее.
//
// Параметры:
//  КлючевоеПоле		- Строка - поле, которое будет ключом для объекта соответствия
//  ФильтрНазначения	- Строка - назначение поля, по которому могут быть отсечены лишние строки. Предусмотренные значения
//						10 - поля владельца сертификата или запроса на сертификат
//						hash алгоритмы хеш,
//						dec алгоритмы шифрования,
//						X509 алгоритмы подписи.
//	
// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//    * Ключ				- Строка
//    * Значение			- Структура:
//      ** OID			- Строка - идентификатор объекта
//      ** Имя			- Строка - строковый идентификатор поля
//      ** Длина		- Число - рекомендуемая длина поля
//      ** Представление- Строка - идентификатор поля для БСП
//      ** Обязательно	- Булево - Истина, если поле должно быть присутствовать в запросе на сертификат
//      ** Назначение	- Строка - основное использования данного поля
//
Функция ОпределитьТип_OID(Знач КлючевоеПоле = Неопределено, ФильтрНазначения = "") Экспорт
	
	Результат = Новый Соответствие;
	
	ВесьМассив = Новый Массив;
	ВесьМассив.Добавить(НовыйАтрибут("2.5.4.3", "CN", 64, "ОбщееИмя", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.4.4", "SN", 64, "Фамилия", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.4.42", "G", 64, "ИмяОтчество", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.4.6", "C", 2, "Страна", "10"));// RU
	ВесьМассив.Добавить(НовыйАтрибут("2.5.4.8", "S", 128, "КодРегиона", "10"));// 77 г. Москва
	ВесьМассив.Добавить(НовыйАтрибут("2.5.4.7", "L", 128, "НаселенныйПункт", "10"));// "Москва г, Зеленоград г, Крюково п"
	ВесьМассив.Добавить(НовыйАтрибут("2.5.4.9", "Street", 128, "Улица", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.100.5", "OGRNIP", 15, "ОГРНИП", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.4.10", "O", 64, "Организация", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.4.11", "OU", 64, "Подразделение", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.4.12", "T", 64, "Должность", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.100.1", "OGRN", 13, "ОГРН", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.100.3", "SNILS", 14, "СНИЛС", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.3.131.1.1", "INN", 12, "ИНН", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.840.113549.1.9.1", "E", 128, "ЭлектроннаяПочта", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.100.4", "INNLE", 10, "ИННЮЛ", "10"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.100.114", "identificationKind", 1, "ВидИдентификации", "10"));
	
	ВесьМассив.Добавить(НовыйАтрибут("2.5.4.5", "SerialNumber", 0, "СерийныйНомер"));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.4.13", "Description", 0, "Описание"));
	
	ВесьМассив.Добавить(НовыйАтрибут("1.2.840.113549.1.1.1", "rsaEncryption", 0));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.840.113549.1.1.5", "sha1WithRSAEncryption", 0));
	ВесьМассив.Добавить(НовыйАтрибут("1.3.6.1.5.5.7.1.1", "authorityInfoAccess", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.14", "X509v3 Subject Key Identifier", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.15", "X509v3 Key Usage", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.16", "X509v3 Private Key Usage Period", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.17", "X509v3 Subject Alternative Name", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.18", "X509v3 Issuer Alternative Name", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.19", "X509v3 Basic Constraints", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.30", "X509v3 Name Constraints", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.31", "X509v3 CRL Distribution Points", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.32", "X509v3 Certificate Policies Extension", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.33", "X509v3 Policy Mappings", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.35", "X509v3 Authority Key Identifier", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.36", "X509v3 Policy Constraints", 0));
	ВесьМассив.Добавить(НовыйАтрибут("2.5.29.37", "X509v3 Extended Key Usage", 0));
	
	ВесьМассив.Добавить(НовыйАтрибут("1.2.840.113549.1.7.3", "КонтейнерЗашифрованногоБлока", 0));
	
	// алгоритмы хеш
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.2.2.9", "GOST R 34.11-94", 0, , "hash"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.7.1.1.2.1", "GOST R 34.11-94", 0, , "hash"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.7.1.1.2.2", "GOST R 34.11-2012-256", 0, , "hash"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.7.1.1.2.3", "GOST R 34.11-2012-512", 0, , "hash"));
	
	// алгоритмы шифрования
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.2.2.21", "GOST R 28147-89", 0, , "dec"));
	
	// алгоритмы экспорта / импорта ключей
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.2.2.19", "GOST R 34.10-2001", 0, , "X509"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.2.2.20", "GOST R 34.10-94", 0, , "X509"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.7.1.1.1.1", "GOST R 34.10-2012-256", 0, , "X509"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.7.1.1.1.2", "GOST R 34.10-2012-512", 0, , "X509"));
	
	// алгоритмы цифровой подписи
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.7.1.1.3.2", "GOST R 34.10-2012-256 + GOST R 34.11-2012-256", 0, , "X509"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.7.1.1.3.3", "GOST R 34.10-2012-512 + GOST R 34.11-2012-512", 0, , "X509"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.2.2.3", "GOST R 34.10-2001", 0, , "X509"));
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.2.2.4", "GOST R 34.10-94", 0, , "X509"));
                                      
	ВесьМассив.Добавить(НовыйАтрибут("1.2.643.100.111", "СредствоЭлектроннойПодписи", 0, , "X509"));
	
	Если НЕ ЗначениеЗаполнено(КлючевоеПоле) Тогда
		КлючевоеПоле = "OID"
	КонецЕсли;
	
	Результат = Новый Соответствие;
	Для каждого СтрокаМассива Из ВесьМассив Цикл
		Если НЕ ЗначениеЗаполнено(ФильтрНазначения) 
			ИЛИ СтрокаМассива.Назначение = ФильтрНазначения Тогда
			Результат.Вставить(СтрокаМассива[КлючевоеПоле], СтрокаМассива);
		КонецЕсли	
	КонецЦикла;	
		
	Возврат Результат;
	
КонецФункции

// Функция для составления реестра сертификатов с помощью которых было зашифровано сообщение.
//
// Параметры:
//  ДанныеФайла - ДвоичныеДанные - содержит зашифрованные данные
//
// Возвращаемое значение:
//   Массив из см. СведенияСертификатаВладельца
//
Функция ПолучитьСертификатыШифрования(ДанныеФайла) Экспорт
	
	Результат = Новый Массив;
	
	СтруктураФайла = ЧтениеСтруктурыASNФайла(ДанныеФайла);
	Если СтруктураФайла = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	ВеткаСтруктуры = НайтиOID(СтруктураФайла, "1.2.840.113549.1.7.3");
	
	Если ВеткаСтруктуры = Неопределено 
		ИЛИ ВеткаСтруктуры.Уровень <> 1 Тогда
		Возврат Результат;
	КонецЕсли;	
	
	МассивДанных = СвойствоСтруктуры(ВеткаСтруктуры.Ветка, "Значение[1].Значение[0].Значение[1].Значение", Новый Массив);
	
	Если МассивДанных = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	СтруктураВетки = Новый Соответствие;
	
	Для каждого СтрокаМассива Из МассивДанных Цикл
		ДанныеВетки = НайтиПоТипуУзла(СтрокаМассива, "INTEGER");
		Если ДанныеВетки <> Неопределено И ДанныеВетки.Уровень = 1 Тогда
			Сертификат = СведенияСертификатаВладельца();
			Для каждого СтрокаСертификата Из ДанныеВетки.Ветка.Значение Цикл
				Если СтрокаСертификата.Тип = "INTEGER" Тогда
					Сертификат.СерийныйНомер = Привести_ЧислоHEX(СтрокаСертификата.Значение);
				ИначеЕсли ТипЗнч(СтрокаСертификата.Значение) = Тип("Массив") Тогда
					СтруктураВетки = ЗаполнитьДанныеОбъектов(СтрокаСертификата.Значение);
					ПолучитьПредставлениеОбъектов(СтруктураВетки, Сертификат, "10");
				КонецЕсли;
			КонецЦикла;	
			Результат.Добавить(Сертификат);
		КонецЕсли;	
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции	

// Функция для анализа дополнительных свойств сертификата.
//
// Параметры:
//  ДанныеФайла - ДвоичныеДанные - содержит данные сертификата
//
// Возвращаемое значение:
//   см. СвойстваСертификатаПоУмолчанию
//
Функция ПолучитьСвойстваСертификата(ДанныеФайла) Экспорт
	
	Результат = СвойстваСертификатаПоУмолчанию();
	
	СтруктураФайла = ЧтениеСтруктурыASNФайла(ДанныеФайла, Новый Структура("ИсключитьСодержимое, ВключитьИсходныеДанные", Ложь, Истина));
	Если СтруктураФайла = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	Результат.Отпечаток = СервисКриптографииDSSСлужебныйВызовСервера.ВычислитьХешДвоичныхДанных(ДанныеФайла, "SHA1");
	
	ПервыйТег = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Тип");
 	ВторойТег = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Тип");
 	ТретийТег = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[0].Тип");
 	ЧетвертыйТег = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[0].Значение[0].Тип");
	
	Если ПервыйТег <> "SEQUENCE"
		ИЛИ ВторойТег <> "SEQUENCE" Тогда
		Возврат Результат
	КонецЕсли;
	
	Если ТретийТег = "OPTIONAL"
		И ЧетвертыйТег = "INTEGER" Тогда
		Версия = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[0].Значение[0].Значение", -1);
		Если Версия >= 0 И Версия < 3 Тогда
			Результат.Версия = Версия + 1	
		КонецЕсли;	
	КонецЕсли;
	
	Если ПервыйТег = "SEQUENCE" Тогда
		ДанныеОбъекта = ЗаполнитьДанныеОбъектов(СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[3].Значение"));
		ПолучитьПредставлениеОбъектов(ДанныеОбъекта, Результат.Издатель, "10");
		ДанныеОбъекта = ЗаполнитьДанныеОбъектов(СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[5].Значение"));
		ПолучитьПредставлениеОбъектов(ДанныеОбъекта, Результат.Субъект, "10");
		ДанныеОбъекта = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[4].Значение");
		Если ДанныеОбъекта <> Неопределено Тогда
			Результат.ДатаНачала = ДанныеОбъекта[0].Значение;
			Результат.ДатаОкончания = ДанныеОбъекта[1].Значение;
		КонецЕсли;
	КонецЕсли;	

	ТретийТег = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[1].Тип");
	Если ТретийТег = "INTEGER" Тогда
		СерийныйНомер = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[1].Значение", 0);
		Если СерийныйНомер > 0 Тогда
			Результат.СерийныйНомер = Привести_ЧислоHEX(СерийныйНомер);
		КонецЕсли;	
	КонецЕсли;	
	
	ТретийТег = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[1].Значение[0].Тип");
	Если ТретийТег = "OBJECT" Тогда
		АлгоритмПубличногоКлюча = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[1].Значение[0].Значение");
		ВсеАлгоритмы = ОпределитьТип_OID(Неопределено, "X509");
		ОпределениеАлгоритма = ВсеАлгоритмы[АлгоритмПубличногоКлюча];
		Если ОпределениеАлгоритма <> Неопределено Тогда
			Результат.АлгоритмПубличногоКлюча = ОпределениеАлгоритма.Имя; // Строка
		КонецЕсли;	
	КонецЕсли;	
	
	ТретийТег = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[6].Значение[1].Тип");
	Если ТретийТег = "BITSTRING" Тогда
		ОткрытыйКлюч = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[6].Значение[1]");
		Результат.ОткрытыйКлюч = ИзвлечьДанныеСообщения(ДанныеФайла, ОткрытыйКлюч.Данные, 1);
	КонецЕсли;	
	
	ВеткаСтруктуры = НайтиOID(СтруктураФайла, "1.2.643.100.111");
	Если ВеткаСтруктуры <> Неопределено Тогда
		Результат.Программа = СвойствоСтруктуры(ВеткаСтруктуры.Ветка, "Значение[1].Значение.Значение", "");
	КонецЕсли;	
	
	ВеткаСтруктуры = НайтиOID(СтруктураФайла, "2.5.29.15");
	Если ВеткаСтруктуры <> Неопределено Тогда
		ЗначениеФлагов = СвойствоСтруктуры(ВеткаСтруктуры.Ветка, "Значение[2].Значение.Значение", "");
		Результат.ИспользоватьДляШифрования = Сред(ЗначениеФлагов, 4, 1) = "1";
		Результат.ИспользоватьДляПодписи = Сред(ЗначениеФлагов, 1, 1) = "1";
		Результат.ФлагиИспользования = ЗначениеФлагов;
	КонецЕсли;	
	
	ВсеАлгоритмы = ОпределитьТип_OID(Неопределено, "hash");
	Для Каждого СтрокаМассива Из ВсеАлгоритмы Цикл
		ВеткаСтруктуры = НайтиOID(СтруктураФайла, СтрокаМассива.Ключ);
		Если ВеткаСтруктуры <> Неопределено Тогда
			Результат.АлгоритмыХеш.Добавить(СтрокаМассива.Значение.Имя); // Строка
		КонецЕсли;	
	КонецЦикла;	
	
	Если Результат.АлгоритмыХеш.Количество() = 0 Тогда
		// совмещенный алгоритм
		ВсеАлгоритмы = Новый Соответствие;
		ВсеАлгоритмы.Вставить("1.2.643.2.2.3", НовыйАтрибут("1.2.643.2.2.3", "GOST R 34.11-94", 0, , "X509"));
		ВсеАлгоритмы.Вставить("1.2.643.7.1.1.3.1", НовыйАтрибут("1.2.643.7.1.1.3.1", "GOST R 34.11-94", 0, , "X509"));
		ВсеАлгоритмы.Вставить("1.2.643.7.1.1.3.2", НовыйАтрибут("1.2.643.7.1.1.3.2", "GOST R 34.11-2012-256", 0, , "X509"));
		ВсеАлгоритмы.Вставить("1.2.643.7.1.1.3.3", НовыйАтрибут("1.2.643.7.1.1.3.3", "GOST R 34.11-2012-512", 0, , "X509"));
		Для Каждого СтрокаМассива Из ВсеАлгоритмы Цикл
			ВеткаСтруктуры = НайтиOID(СтруктураФайла, СтрокаМассива.Ключ);
			Если ВеткаСтруктуры <> Неопределено Тогда
				Результат.АлгоритмыХеш.Добавить(СтрокаМассива.Значение.Имя);
			КонецЕсли;	
		КонецЦикла;	
	КонецЕсли;
	
	ВеткаСтруктуры = НайтиOID(СтруктураФайла, "2.5.29.14");
	Если ВеткаСтруктуры <> Неопределено Тогда
		Результат.ИдентификаторСубъекта = СвойствоСтруктуры(ВеткаСтруктуры, "Следующий.Значение.Значение", "");
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Функция для анализа дополнительных свойств запроса на сертификат.
//
// Параметры:
//  ДанныеФайла 		- ДвоичныеДанные - содержит данные сертификата
//  ПараметрыОперации   - Неопределено, Структура - определяет дополнительные параметры функции:
//    * КлючевоеПоле 	- Строка - имя ключевого поля для формирования атрибутов владельца
//
// Возвращаемое значение:
//   см. СервисКриптографииDSSASNКлиентСервер.СвойстваЗапросаНаСертификатПоУмолчанию
//
Функция ПолучитьСвойстваЗапросаНаСертификат(ДанныеФайла, ПараметрыОперации = Неопределено) Экспорт
	
	Результат = СвойстваЗапросаНаСертификатПоУмолчанию();
	КлючевоеПоле = СвойствоСтруктуры(ПараметрыОперации, "КлючевоеПоле", "Представление");
	
	СтруктураФайла = ЧтениеСтруктурыASNФайла(ДанныеФайла, Новый Структура("ИсключитьСодержимое", Ложь));
	Если СтруктураФайла = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	ПервыйТег = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[2].Значение[0].Значение[0].Тип");
 	ВторойТег = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[2].Значение[1].Тип");
	
	Если ПервыйТег <> "OBJECT"
		ИЛИ ВторойТег <> "BITSTRING" Тогда
		Возврат Результат
	КонецЕсли;
	
	ДанныеОбъекта = ЗаполнитьДанныеОбъектов(СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[1].Значение"));
	Результат.Владелец = ПолучитьПредставлениеОбъектов(ДанныеОбъекта, Неопределено, "10", КлючевоеПоле);
	
	АлгоритмПубличногоКлюча = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[2].Значение[0].Значение[0].Значение");
	ВсеАлгоритмы = ОпределитьТип_OID(Неопределено, "X509");
	ОпределениеАлгоритма = ВсеАлгоритмы[АлгоритмПубличногоКлюча];
	Если ОпределениеАлгоритма <> Неопределено Тогда
		Результат.АлгоритмПубличногоКлюча = ОпределениеАлгоритма.Имя;
	КонецЕсли;
	
	ОткрытыйКлюч = СвойствоСтруктуры(СтруктураФайла, "Значение[0].Значение[0].Значение[2].Значение[1].Значение");
	Если ЗначениеЗаполнено(ОткрытыйКлюч) Тогда
		Результат.ОткрытыйКлюч = ОткрытыйКлюч;
	КонецЕсли;	
	
	Возврат Результат;
	
КонецФункции

// Функция для данных из электронной подписи, таких как значение подписи, алгоритм подписи, сертификат подписи.
//
// Параметры:
//  ДанныеФайла 		- ДвоичныеДанные - содержит криптографические данные
//  ПараметрыОперации 	- Структура - проверяется наличие поля "ДанныеСертификата"
//
// Возвращаемое значение:
//  Массив - элементы содержат Структуру с полями:
//    * ЗначениеПодписи			- ДвоичныеДанные
//    * АлгоритмПодписи			- Строка
//    * Издатель					- см. СведенияСертификатаВладельца
//    * АлгоритмХеша				- Строка
//    * АлгоритмШифрованияХеша	- Строка
//    * АлгоритмПубличногоКлюча	- Строка
//    * АлгоритмПодписи 			- Строка
//    * ЗначениеПодписи			- Строка
//    * ЗначениеОткрытогоКлюча	- Строка
//    * Сертификат				- ДвоичныеДанные
//    * СертификатИздателя		- см. СведенияСертификатаВладельца
//    * АтрибутыПодписи			- Строка
//    * ВремяПодписи				- Дата
//    * ХешДокумента				- Строка
//    * СерийныйНомер				- Строка
//    * ТипПодписи				- Строка - возможные варианты "CMS", "CAdES-BES", "CAdES-Av3", "CadES-X Long Type 2", "CadES-С", "CadES-T", "Неопределено"
//    * МеткаВремени			- Дата
//	
Функция ПолучитьСвойстваПодписи(ДанныеФайла, ПараметрыОперации = Неопределено) Экспорт
	
	Результат = Новый Массив;
	НужныСертификаты = СвойствоСтруктуры(ПараметрыОперации, "ДанныеСертификата", Ложь);
	
	СтруктураФайла = ЧтениеСтруктурыASNФайла(ДанныеФайла, Новый Структура("ИсключитьСодержимое, ВключитьИсходныеДанные", Ложь, Истина));
	Если СтруктураФайла = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	ВеткаСтруктуры = НайтиOID(СтруктураФайла, "1.2.840.113549.1.7.2");
	
	Если ВеткаСтруктуры = Неопределено 
		ИЛИ ВеткаСтруктуры.Уровень <> 1 Тогда
		Возврат Результат;
	КонецЕсли;	
	
	ВеткаСтруктуры = НайтиOID(ВеткаСтруктуры.Ветка, "1.2.840.113549.1.7.1");
	
	Если ВеткаСтруктуры = Неопределено ИЛИ ВеткаСтруктуры.Следующий = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;	
	
	// ищем ветку со значением подписи
	ВеткаПодписей = Неопределено;
	ВеткаСертификатов = Неопределено;
	Отсоединенная = Истина;
	НашлиЭлемент = ВеткаСтруктуры.Следующий;
	Если ТипЗнч(НашлиЭлемент.Значение) = Тип("Массив")
		И НашлиЭлемент.Значение.Количество() = 1
		И НашлиЭлемент.Значение[0].Тип = "OCTETSTRING" Тогда
		Отсоединенная = Ложь;
	КонецЕсли;
	
	Если НЕ Отсоединенная Тогда
		Родитель = НашлиЭлемент.Значение[0];
		НашлиЭлемент = СвойствоСтруктуры(Родитель, "Значение[0]");
	Иначе
		Родитель = ВеткаСтруктуры.Ветка;
	КонецЕсли;
	
	Пока НашлиЭлемент <> Неопределено Цикл
		Если ВеткаПодписей = Неопределено И ТипЗнч(НашлиЭлемент.Значение) = Тип("Массив") И НашлиЭлемент.Тип = "SET" Тогда
			ВеткаПодписей = НашлиЭлемент.Значение;
		ИначеЕсли ВеткаСертификатов = Неопределено И ТипЗнч(НашлиЭлемент.Значение) = Тип("Массив") И НашлиЭлемент.Тип = "OPTIONAL" Тогда
			ВеткаСертификатов = НашлиЭлемент.Значение;
		КонецЕсли;
		НашлиЭлемент = ПолучитьСледующийЭлемент(Родитель, НашлиЭлемент);
	КонецЦикла;

	Если ВеткаПодписей = Неопределено ИЛИ ВеткаСертификатов = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	ВсеАлгоритмы = ОпределитьТип_OID(Неопределено);
	СерийныеНомера = Новый Соответствие;
	
	Для Каждого СтрокаМассива Из ВеткаПодписей Цикл
		НоваяСтрока = Новый Структура;
		
		НоваяСтрока.Вставить("АлгоритмХеша", "");
		НоваяСтрока.Вставить("АлгоритмШифрованияХеша", "");
		НоваяСтрока.Вставить("АлгоритмПубличногоКлюча", "");
		НоваяСтрока.Вставить("АлгоритмПодписи", "");
		НоваяСтрока.Вставить("ЗначениеПодписи", "");
		НоваяСтрока.Вставить("ЗначениеОткрытогоКлюча", "");
		НоваяСтрока.Вставить("Сертификат", Неопределено);
		НоваяСтрока.Вставить("СертификатИздателя", СведенияСертификатаВладельца());
		НоваяСтрока.Вставить("АтрибутыПодписи", Неопределено);
		НоваяСтрока.Вставить("ВремяПодписи", Неопределено);
		НоваяСтрока.Вставить("ХешДокумента", Неопределено);
		НоваяСтрока.Вставить("СерийныйНомер", "");
		НоваяСтрока.Вставить("ТипПодписи", "");
		НоваяСтрока.Вставить("МеткаВремени", '00010101');
		
		ТипПодписи = "CMS";
		ДанныеПодписи = СвойствоСтруктуры(СтрокаМассива, "Значение", Новый Массив);
		Счетчик = 1;
		Для Каждого СтрокаПодписи Из ДанныеПодписи Цикл
			
			Если Счетчик = 2 Тогда // подписант и серийный номер
				СерийныйНомер = СвойствоСтруктуры(СтрокаПодписи, "Значение[1].Значение", 0);
				Если СерийныйНомер <> Неопределено Тогда
					НоваяСтрока.СерийныйНомер = Привести_ЧислоHEX(СерийныйНомер);
				КонецЕсли;	
				
			ИначеЕсли СтрокаПодписи.Тип = "SEQUENCE" Тогда
				НайденныйАлгоритм = ВсеАлгоритмы[СвойствоСтруктуры(СтрокаПодписи, "Значение[0].Значение", "")];
				Если НайденныйАлгоритм <> Неопределено И НайденныйАлгоритм.Назначение = "hash" Тогда
					НоваяСтрока.АлгоритмХеша = НайденныйАлгоритм.Имя;
				ИначеЕсли НайденныйАлгоритм <> Неопределено Тогда
					НоваяСтрока.АлгоритмШифрованияХеша = НайденныйАлгоритм.Имя;
				КонецЕсли;	
				
			ИначеЕсли СтрокаПодписи.Тип = "OCTETSTRING" Тогда
				НоваяСтрока.ЗначениеПодписи = СтрокаПодписи.Значение;
				
			ИначеЕсли СтрокаПодписи.Тип = "OPTIONAL" Тогда
				Издатель = НайтиOID(СтрокаПодписи, "1.2.840.113549.1.9.16.2.47");
				ХешДокумента = НайтиOID(СтрокаПодписи, "1.2.840.113549.1.9.4");
				ВремяПодписи = НайтиOID(СтрокаПодписи, "1.2.840.113549.1.9.5");
				
				Если Издатель <> Неопределено Тогда
					ДанныеИздателя = ЗаполнитьДанныеОбъектов(Издатель.Следующий.Значение);
					ПолучитьПредставлениеОбъектов(ДанныеИздателя, НоваяСтрока.СертификатИздателя, "10");
					ТипПодписи = ?(ТипПодписи <> "CMS", ТипПодписи, "CAdES-BES");
				КонецЕсли;
				
				Если ХешДокумента <> Неопределено Тогда
					НоваяСтрока.АтрибутыПодписи = "31" + ИзвлечьДанныеСообщения(ДанныеФайла, СтрокаПодписи.Данные, 1, , Истина); // заменяем тег
					ТекущаяВетка = СвойствоСтруктуры(ХешДокумента.Следующий, "Значение[0].Значение");
					Если ТипЗнч(ТекущаяВетка) = Тип("Структура") Тогда
						НоваяСтрока.ХешДокумента = СвойствоСтруктуры(ТекущаяВетка, "Данные", "");
					Иначе	
						НоваяСтрока.ХешДокумента = СокрЛП(ТекущаяВетка);
					КонецЕсли;	
				КонецЕсли;
				
				Если ВремяПодписи <> Неопределено Тогда
					НоваяСтрока.ВремяПодписи = СвойствоСтруктуры(ВремяПодписи.Следующий, "Значение[0].Значение", "");
				КонецЕсли;
				
			ИначеЕсли СтрокаПодписи.Тип = "CONTEXT" Тогда
				ТипПодписи = "Неопределено";
				
				ВеткаМеткиВремени = НайтиOID(СтрокаПодписи, "1.2.840.113549.1.9.16.2.14");
				ВеткаОписаниеСертификатов = НайтиOID(СтрокаПодписи, "1.2.840.113549.1.9.16.2.21");
				ВеткаОписаниеОтзыва = НайтиOID(СтрокаПодписи, "1.2.840.113549.1.9.16.2.22");
				ВеткаЗначениеСертификатов = НайтиOID(СтрокаПодписи, "1.2.840.113549.1.9.16.2.23");
				ВеткаЗначениеОтзыва = НайтиOID(СтрокаПодписи, "1.2.840.113549.1.9.16.2.24");
				ВеткаСписокОтзываСерверов = НайтиOID(СтрокаПодписи, "1.2.840.113549.1.9.16.2.26");
				ВеткаАрхива = НайтиOID(СтрокаПодписи, "0.4.0.1733.2.5");
				
				Если ВеткаАрхива <> Неопределено Тогда
					ТипПодписи = "CAdES-Av3";
				ИначеЕсли ВеткаМеткиВремени <> Неопределено
					И ВеткаОписаниеСертификатов <> Неопределено
					И ВеткаОписаниеОтзыва <> Неопределено
					И ВеткаЗначениеСертификатов <> Неопределено
					И ВеткаЗначениеОтзыва <> Неопределено
					И ВеткаСписокОтзываСерверов <> Неопределено Тогда
					ТипПодписи = "CadES-X Long Type 2";
				ИначеЕсли ВеткаМеткиВремени <> Неопределено
					И (ВеткаОписаниеСертификатов <> Неопределено И ВеткаОписаниеСертификатов.Уровень = 1)
					И (ВеткаОписаниеОтзыва <> Неопределено И ВеткаОписаниеОтзыва.Уровень = 1) Тогда
					ТипПодписи = "CadES-С";
				ИначеЕсли ВеткаМеткиВремени <> Неопределено Тогда
					ТипПодписи = "CadES-T";
				КонецЕсли;
				
				Если ВеткаМеткиВремени <> Неопределено Тогда
					НужныйРаздел = НайтиOID(ВеткаМеткиВремени.Следующий, "1.2.840.113549.1.9.16.1.4");
					ДеталиРаздела = СвойствоСтруктуры(НужныйРаздел.Следующий, "Значение[0].Значение.Значение[0].Значение", Новый Массив);
					Для Каждого СтрокаРаздела Из ДеталиРаздела Цикл
						Если СтрокаРаздела.Тип = "GENERALIZEDTIME" Тогда
				    		НоваяСтрока.МеткаВремени = СтрокаРаздела.Значение;
							Прервать;
						КонецЕсли;
					КонецЦикла;
				КонецЕсли;
				
			КонецЕсли;
			
			Счетчик = Счетчик + 1;
			
		КонецЦикла;
		
		НоваяСтрока.ТипПодписи = ТипПодписи;
		СерийныеНомера.Вставить(НоваяСтрока.СерийныйНомер, НоваяСтрока);
		Результат.Добавить(НоваяСтрока);

	КонецЦикла;
	
	Для Каждого СтрокаМассива Из ВеткаСертификатов Цикл
		НоваяСтрока = Неопределено;
		СертификатПодписанта = СвойствоСтруктуры(СтрокаМассива, "Значение[0].Значение", Новый Массив);
		
		Если СертификатПодписанта.Количество() > 0 Тогда
			СерийныйНомер = Привести_ЧислоHEX(СвойствоСтруктуры(СертификатПодписанта, "[1].Значение", 0));
			НоваяСтрока = СерийныеНомера[СерийныйНомер];
		КонецЕсли;
		
		Если НоваяСтрока <> Неопределено Тогда
			Если НужныСертификаты Тогда
				НоваяСтрока.Сертификат = ИзвлечьДанныеСообщения(ДанныеФайла, СтрокаМассива.Данные, 0, "ДвоичныеДанные", Истина);
			КонецЕсли;
			ЗначениеОткрытогоКлюча = СвойствоСтруктуры(СертификатПодписанта, "[6].Значение[1].Данные", "");
			НоваяСтрока.ЗначениеОткрытогоКлюча = ИзвлечьДанныеСообщения(ДанныеФайла, ЗначениеОткрытогоКлюча, 1);
			ОпределениеАлгоритма = ВсеАлгоритмы[СвойствоСтруктуры(СертификатПодписанта, "[2].Значение[0].Значение", "")];
			Если ОпределениеАлгоритма <> Неопределено Тогда
				НоваяСтрока.АлгоритмПодписи = ОпределениеАлгоритма.Имя;
			КонецЕсли;
			ДанныеВетки = СвойствоСтруктуры(СтрокаМассива, "Значение[1].Значение[0].Значение", "");
			ОпределениеАлгоритма = ВсеАлгоритмы[ДанныеВетки];
			Если ОпределениеАлгоритма <> Неопределено Тогда
				НоваяСтрока.АлгоритмПубличногоКлюча = ОпределениеАлгоритма.Имя;
			КонецЕсли;
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

// Анализирует тип криптографических данных
//
// Параметры:
//  ДанныеФайла - ДвоичныеДанные - содержит криптографические данные
//
// Возвращаемое значение:
//  Строка - фиксированные варианты Неопределено, Подпись, Шифрованные
//	
Функция ОпределитьТипКриптоДанных(ДанныеФайла) Экспорт
	
	Результат = "Неопределено";
	СтруктураФайла = ЧтениеСтруктурыASNФайла(ДанныеФайла);
	Если СтруктураФайла = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	ВеткаШифрования = НайтиOID(СтруктураФайла, "1.2.840.113549.1.7.3");
	ВеткаПодписи = НайтиOID(СтруктураФайла, "1.2.840.113549.1.7.2");
	
	Если ВеткаШифрования <> Неопределено Тогда
		Результат = "Шифрованные";
	ИначеЕсли ВеткаПодписи <> Неопределено Тогда
		Результат = "Подпись";
	КонецЕсли;	
	
	Возврат Результат;
	
КонецФункции

// Меняет местами байты в паре байтов (слово)
//
// Параметры:
//  ДанныеФайла - ДвоичныеДанные - содержит данные для обработки
//
// Возвращаемое значение:
//  ДвоичныеДанные
//
Функция ИнвертироватьПолубайты(ДанныеФайла) Экспорт
	
	СтрокаДанных = ПолучитьHexСтрокуИзДвоичныхДанных(ДанныеФайла);
	СтрокаРезультата = "";
	Счетчик = 1;
	ДлинаСтроки = СтрДлина(СтрокаДанных);
	
	Пока Счетчик < ДлинаСтроки Цикл
		СтрокаРезультата = СтрокаРезультата + Сред(СтрокаДанных, Счетчик + 1, 1) + Сред(СтрокаДанных, Счетчик, 1);
		Счетчик = Счетчик + 2;
	КонецЦикла;
	
	Если ДлинаСтроки % 2 = 1 Тогда
		СтрокаРезультата = СтрокаРезультата + "0" + Сред(СтрокаДанных, ДлинаСтроки, 1);
	КонецЕсли;
	
	Результат = ПолучитьДвоичныеДанныеИзHexСтроки(СтрокаРезультата);
	
	Возврат Результат;
	
КонецФункции

// Формирует структуру, описывающую свойства сертификата ЭП.
//
// Возвращаемое значение:
//  Структура:
//    * Версия					- Строка 
//    * СерийныйНомер			- Строка
//    * ОткрытыйКлюч			- Строка
//    * Отпечаток				- Строка
//    * ДатаНачала				- Дата
//    * ДатаОкончания			- Дата
//    * Издатель				- см. СведенияСертификатаВладельца
//    * Субъект					- см. СведенияСертификатаВладельца
//    * ИспользоватьДляПодписи	- Булево
//    * ИспользоватьДляШифрования- Булево
//    * РасширенныеСвойства		- Структура
//    * АлгоритмПубличногоКлюча	- Строка
//    * Программа				- Строка
//    * АлгоритмыХеш			- Строка
//    * ИдентификаторСубъекта	- Строка
//    * ФлагиИспользования		- Строка - единицы или нули с позиционным кодирование
//				digitalSignature(0) цифровая подпись,
//				nonRepudiation(1) non-repudiation,
//				keyEncipherment(2) шифрование ключей,
//				dataEncipherment(3) шифрование данных,
//				keyAgreement(4) согласование ключей,
//				keyCertSign(5) проверка подписей под квалифицированными сертификатами,
//				cRLSign(6) проверка подписей под списками аннулированных сертификатов,
//				encipherOnly(7) зашифрование данных в процессе согласования ключей,
//				decipherOnly(8) расшифрование данных в процессе согласования ключей.
//
Функция СвойстваСертификатаПоУмолчанию() Экспорт
	
	Результат = Новый Структура();
	// совпадающие с платформой
	Результат.Вставить("Версия", "");
	Результат.Вставить("СерийныйНомер", "");
	Результат.Вставить("ОткрытыйКлюч", "");
	Результат.Вставить("Отпечаток", "");
	Результат.Вставить("Издатель", СведенияСертификатаВладельца());
	Результат.Вставить("Субъект", СведенияСертификатаВладельца());
	Результат.Вставить("ДатаНачала", '00010101');
	Результат.Вставить("ДатаОкончания", '00010101');
	Результат.Вставить("ИспользоватьДляПодписи", Ложь);
	Результат.Вставить("ИспользоватьДляШифрования", Ложь);
	Результат.Вставить("РасширенныеСвойства", Новый Структура);
	
	// дополнительные
	Результат.Вставить("ИдентификаторСубъекта", "");
	Результат.Вставить("Программа", "");
	Результат.Вставить("АлгоритмПубличногоКлюча", "");
	Результат.Вставить("АлгоритмыХеш", Новый Массив);
	Результат.Вставить("ФлагиИспользования", "");
	
	Возврат Результат;
	
КонецФункции

// Формирует описание сведений владельца сертификата + серийный номер
//
// Возвращаемое значение:
//  Структура:
//    * СерийныйНомер 	- Строка
//    * ОбщееИмя		- Строка
//    * Фамилия 		- Строка
//    * ИмяОтчество		- Строка
//    * Страна			- Строка
//    * КодРегиона		- Строка
//    * НаселенныйПункт	- Строка
//    * Улица			- Строка
//    * Организация		- Строка
//    * ЭлектроннаяПочта- Строка
//    * Подразделение	- Строка
//    * Должность		- Строка
//    * ЭлектроннаяПочта- Строка
//    * ОГРНИП			- Строка
//    * ОГРН			- Строка
//    * СНИЛС			- Строка
//    * ИНН				- Строка
//
Функция СведенияСертификатаВладельца() Экспорт
	
	Результат = Новый Структура();
	Результат.Вставить("СерийныйНомер", "");
	Результат.Вставить("ОбщееИмя", "");
	Результат.Вставить("Фамилия", "");
	Результат.Вставить("ИмяОтчество", "");
	Результат.Вставить("Страна", "");
	Результат.Вставить("КодРегиона", "");
	Результат.Вставить("НаселенныйПункт", "");
	Результат.Вставить("Улица", "");
	Результат.Вставить("ОГРНИП", "");
	Результат.Вставить("Организация", "");
	Результат.Вставить("ЭлектроннаяПочта", "");
	Результат.Вставить("Подразделение", "");
	Результат.Вставить("Должность", "");
	Результат.Вставить("ОГРН", "");
	Результат.Вставить("СНИЛС", "");
	Результат.Вставить("ИНН", "");
	
	Возврат Результат;
	
КонецФункции

// Формирует структуру описывающая свойства запроса на сертификат ЭП.
//
// Возвращаемое значение:
//  Структура
//
Функция СвойстваЗапросаНаСертификатПоУмолчанию() Экспорт
	
	Результат = Новый Структура();
	Результат.Вставить("ОткрытыйКлюч", "");
	Результат.Вставить("АлгоритмПубличногоКлюча", "");
	Результат.Вставить("Владелец", Новый Соответствие);
	
	Возврат Результат;
	
КонецФункции

#КонецОбласти

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

#Область АнализASN1

Функция ОпределитьТип_Теги()
	
	Результат = Новый Соответствие;
	
	Результат.Вставить(0, "OPTIONAL");
	Результат.Вставить(1, "BOOLEAN");
	Результат.Вставить(2, "INTEGER");
	Результат.Вставить(3, "BITSTRING");
	Результат.Вставить(4, "OCTETSTRING");
	Результат.Вставить(5, "NULL");
	Результат.Вставить(6, "OBJECT");
	Результат.Вставить(7, "OBJECTDISCTRIPTION");
	Результат.Вставить(8, "INSTANCEOF");
	Результат.Вставить(9, "REAL");
	Результат.Вставить(10, "ENUMERATED");
	Результат.Вставить(11, "EMBEDDEDPVD");
	Результат.Вставить(12, "UTF8");
	Результат.Вставить(13, "RELATIVEOID");
	Результат.Вставить(16, "SEQUENCE");
	Результат.Вставить(17, "SET");
	Результат.Вставить(18, "NUMERICSTRING");
	Результат.Вставить(19, "PRINTABLESTRING");
	Результат.Вставить(20, "TELETEXSTRING");
	Результат.Вставить(21, "VIDEOSTRING");
	Результат.Вставить(22, "IA5STRING");
	Результат.Вставить(23, "UTCTIME");
	Результат.Вставить(24, "GENERALIZEDTIME");
	Результат.Вставить(25, "GRAFICSTRING");
	Результат.Вставить(26, "VISIBLESTRING");
	Результат.Вставить(27, "GENERALSTRING");
	Результат.Вставить(28, "UNIVERSALSTRING");
	Результат.Вставить(29, "CHARACTERSTRING");
	Результат.Вставить(30, "UNICODE");
	Результат.Вставить(35, "BITSTRING");
	
	Возврат Результат;
	
КонецФункции
	
Функция ОпределитьТип_Классы()
	
	Результат = Новый Соответствие;
	
	Результат.Вставить(0, "U");
	Результат.Вставить(64, "A");
	Результат.Вставить(128, "C");
	Результат.Вставить(192, "P");
	
	Возврат Результат;
	
КонецФункции

Функция ОпределитьТип_Тип()
	
	Результат = Новый Соответствие;
	
	Результат.Вставить(0, "Primitive");
	Результат.Вставить(32, "Constructed");
	
	Возврат Результат;
	
КонецФункции

Функция ОпределитьТип_HEX()
	
	Возможные = Новый Соответствие;
	Возможные.Вставить(0, "0");
	Возможные.Вставить(1, "1");
	Возможные.Вставить(2, "2");
	Возможные.Вставить(3, "3");
	Возможные.Вставить(4, "4");
	Возможные.Вставить(5, "5");
	Возможные.Вставить(6, "6");
	Возможные.Вставить(7, "7");
	Возможные.Вставить(8, "8");
	Возможные.Вставить(9, "9");
	Возможные.Вставить(10, "A");
	Возможные.Вставить(11, "B");
	Возможные.Вставить(12, "C");
	Возможные.Вставить(13, "D");
	Возможные.Вставить(14, "E");
	Возможные.Вставить(15, "F");

	Возврат Возможные;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//    * OID			- Строка
//    * Имя			- Строка
//    * ДлинаПоля		- Число
//    * Представление	- Строка
//    * Назначение	- Строка
//    * Обязательно	- Булево
//
Функция НовыйАтрибут(OID, ИмяПоля, ДлинаПоля, Представление = "", Назначение = "", Обязательно = Ложь)
	
	Результат = Новый Структура();
	Результат.Вставить("OID", OID);
	Результат.Вставить("Имя", ИмяПоля);
	Результат.Вставить("Длина", ?(ЗначениеЗаполнено(ДлинаПоля), ДлинаПоля, 0));
	Результат.Вставить("Представление", Представление);
	Результат.Вставить("Обязательно", Обязательно);
	Результат.Вставить("Назначение", Назначение);
	
	Возврат Результат;
	
КонецФункции

Функция ПолучитьТипТега(ОпределениеASN, ДанныеФайла)
	
	Если ПолучитьТипКласса(ОпределениеASN, ДанныеФайла) = "C" И ДанныеФайла.Блок <> 0 Тогда
		НашлиЗначение = "CONTEXT";
	Иначе	
		НашлиЗначение = ОпределениеASN.Теги[ДанныеФайла.Блок];
	КонецЕсли;
	
	Если НашлиЗначение <> Неопределено Тогда
		Возврат НашлиЗначение;
	Иначе
		Возврат Формат(ДанныеФайла, "ЧЦ=2");
	КонецЕсли;	
		
КонецФункции

Функция ПолучитьТипКласса(ОпределениеASN, ДанныеФайла)
	
	НашлиЗначение = ОпределениеASN.Классы[ДанныеФайла.Класс];
	Если НашлиЗначение <> Неопределено Тогда
		Возврат НашлиЗначение;
	Иначе
		ВызватьИсключение НСтр("ru = 'Ошибка определения класса'");
	КонецЕсли;	
	
КонецФункции

Функция ПолучитьТип(ОпределениеASN, ДанныеФайла)
	
	Результат = ОпределениеASN.Типы[ДанныеФайла.Тип];
	Возврат Результат;
	
КонецФункции

Функция ПолучитьСледующийБайт(ОпределениеASN, ПотокБайтов, ТекущийСчетчик)
	
	Если ПотокБайтов.Размер >= ТекущийСчетчик Тогда
		ТекущийБайт = ПотокБайтов.Получить(ТекущийСчетчик - 1);
		ТекущийСчетчик = ТекущийСчетчик + 1;
	Иначе
		ВызватьИсключение НСтр("ru = 'Неожиданный конец файла'");
	КонецЕсли;
	
	Возврат ТекущийБайт;
	
КонецФункции

Функция ПолучитьСледующиеБайты(ОпределениеASN, ПотокБайтов, ТекущийСчетчик, Количество = 0, ПустыеБайты = Ложь)
	
	Если ПустыеБайты Тогда
		ПредыдущийБайт	= Неопределено;
		ТекущийБайт 	= ПотокБайтов.Получить(ТекущийСчетчик - 1);
		Пока Истина Цикл
			Количество		= Количество + 1;
			ПредыдущийБайт	= ТекущийБайт;
			ТекущийБайт 	= ПотокБайтов.Получить(ТекущийСчетчик + Количество - 1);
			Если ТекущийБайт = 0 И ПредыдущийБайт = 0 Тогда
				Количество = Количество - 2;
				Прервать;
			КонецЕсли;	
		КонецЦикла;
	КонецЕсли;
	
	Если ПотокБайтов.Размер >= (Количество + ТекущийСчетчик - 1) Тогда
		Если Количество = 0 Тогда
			Результат = Новый БуферДвоичныхДанных(0);
		Иначе	
			Результат = ПотокБайтов.ПолучитьСрез(ТекущийСчетчик - 1, Количество);
		КонецЕсли;	
		ТекущийСчетчик = ТекущийСчетчик + Количество;
		
	Иначе
		ВызватьИсключение НСтр("ru = 'Неожиданный конец файла'");
	КонецЕсли;	
	
	Возврат Результат;
	
КонецФункции

Функция ПолучитьТекущийТег(ОпределениеASN, ПотокБайтов, ТекущийСчетчик)
	
	ТекущийБайт = ПолучитьСледующийБайт(ОпределениеASN, ПотокБайтов, ТекущийСчетчик);
	Класс 	= ПобитовоеИ(ТекущийБайт, 192);// 0xc0 (класс блока)
	Тип 	= ПобитовоеИ(ТекущийБайт, 32);// 0x20 (примитивный или составной)
	Блок	= ПобитовоеИ(ТекущийБайт, 31);// 0x1f (тип примитивного блока)
	
	Если Блок = 21 Тогда // 0x1f
		Блок = 0;
		Пока Истина Цикл
			ТекущийБайт = ПолучитьСледующийБайт(ОпределениеASN, ПотокБайтов, ТекущийСчетчик);
			Блок = ПобитовоеИли(ПобитовыйСдвигВлево(Блок, 7), 127);//  0x7f
			Если ПобитовоеИ(ТекущийБайт, 128) <> 0 Тогда //  0x80
				Прервать;
			КонецЕсли;	
		КонецЦикла;	
	КонецЕсли;
	
	Возврат Новый Структура("Блок, Тип, Класс", Блок, Тип, Класс);
	
КонецФункции

Функция ПолучитьТекущуюДлину(ОпределениеASN, ПотокБайтов, ТекущийСчетчик)
	
	Результат 	= 0;
	ТекущийБайт = ПолучитьСледующийБайт(ОпределениеASN, ПотокБайтов, ТекущийСчетчик);
	
	Если ПроверитьБит(ТекущийБайт, 7) Тогда
		Количество = ПобитовоеИ(ТекущийБайт, 127);
		Если Количество = 127 Тогда
			ВызватьИсключение НСтр("ru = 'ASN.1 syntax error'");
		КонецЕсли;
		
		Если Количество = 0 Тогда
			Результат = -1;
		ИначеЕсли Количество > ПотокБайтов.Размер Тогда
			Результат = 0;
		Иначе	
			МассивБайтов = ПолучитьСледующиеБайты(ОпределениеASN, ПотокБайтов, ТекущийСчетчик, Количество);
			Для каждого СтрокаМассива Из МассивБайтов Цикл
				Результат 	= ПобитовоеИли(ПобитовыйСдвигВлево(Результат, 8), СтрокаМассива);
			КонецЦикла;
		КонецЕсли;	
		
		Попытка
			Результат = Число(Результат);
		Исключение
			Результат = 0;
		КонецПопытки;
		
	Иначе
		Результат = ТекущийБайт;
		
	КонецЕсли;	
	
	Возврат Результат;
	
КонецФункции

Функция Привести_Булево(МассивБайтов)
	
	Результат = Ложь;
	
	Если МассивБайтов.Размер <> 1 Тогда
		ВызватьИсключение НСтр("ru = 'ASN.1 syntax error'");
	ИначеЕсли МассивБайтов[0] <> 0 Тогда
		Результат = Истина;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

Функция Привести_Целое(МассивБайтов)
	
	Результат		= 0;
	ЗначенияБайтов 	= Ложь;
	Количество		= МассивБайтов.Размер;
	Если Количество > 1 Тогда
		ЗначенияБайтов = МассивБайтов[0] = 255 И ПобитовоеИ(МассивБайтов[1], 128);
		ЗначенияБайтов = ЗначенияБайтов ИЛИ (МассивБайтов[0] = 0 И НЕ ПобитовоеИ(МассивБайтов[1], 128));
	ИначеЕсли Количество = 0 Тогда
		ЗначенияБайтов = Истина;
	КонецЕсли;
	
	Если ЗначенияБайтов Тогда
		ВызватьИсключение НСтр("ru = 'ASN.1 syntax error'");
	КонецЕсли;	
		
	ОтрицательноеЗначение = ПроверитьБит(МассивБайтов[0], 8);
	Если ОтрицательноеЗначение Тогда
		Для Счетчик = 1 По Количество Цикл
			МассивБайтов[Счетчик - 1] = 255 - МассивБайтов[Счетчик - 1];
		КонецЦикла;	
		Счетчик = Счетчик - 1;
		Пока Счетчик > 0 Цикл
			МассивБайтов[Счетчик] = МассивБайтов[Счетчик] + 1;
			Если МассивБайтов[Счетчик] <= 255 Тогда
				Прервать;
			КонецЕсли;	
			МассивБайтов[Счетчик] = 0;
			Счетчик = Счетчик - 1;
		КонецЦикла;	
	КонецЕсли;
	
	Для каждого СтрокаМассива Из МассивБайтов Цикл
		Результат = Результат * 256 + СтрокаМассива;
	КонецЦикла;
	
	Если ОтрицательноеЗначение Тогда
		Результат = - Результат;	
	КонецЕсли;
	
	Попытка
		Результат = Число(Результат);
	Исключение
		Результат = 0;
	КонецПопытки;
	
	Возврат Результат;
	
КонецФункции

Функция Привести_Пустое(МассивБайтов)
	
	Результат = Неопределено;
	
	Если МассивБайтов.Размер <> 0 Тогда
		ВызватьИсключение НСтр("ru = 'ASN.1 syntax error'");
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

Функция Привести_Строка(МассивБайтов)
	
	Результат = "";
	
	Для каждого СтрокаМассива Из МассивБайтов Цикл
		Если СтрокаМассива > 31 Тогда // Лучше использовать НайтиНедопустимыеСимволыXML, но в веб-клиенте не работает
			СледующийСимвол = Символ(СтрокаМассива);
			Результат = Результат + СледующийСимвол;
		КонецЕсли;	
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

Функция Привести_Дата(МассивБайтов, Алгоритм = "Z")
	
	ПредставлениеДаты = Привести_Строка(МассивБайтов);
	Результат = '00010101';
	
	Попытка
		Если Алгоритм = "Z" Тогда
			ГодДаты = Сред(ПредставлениеДаты, 1, 2);
			МесяцДаты = Сред(ПредставлениеДаты, 3, 2);
			ДеньДаты = Сред(ПредставлениеДаты, 5, 2);
			ЧасДаты = Сред(ПредставлениеДаты, 7, 2);
			МинутыДаты = Сред(ПредставлениеДаты, 9, 2);
			СекундыДаты = Сред(ПредставлениеДаты, 11, 2);
			
			Если Число(ГодДаты) > 49 Тогда
				ГодДаты = "19" + ГодДаты;
			Иначе
				ГодДаты = "20" + ГодДаты;
			КонецЕсли;
			
			Результат = Дата(ГодДаты + МесяцДаты + ДеньДаты + ЧасДаты + МинутыДаты + СекундыДаты);
		КонецЕсли;
		
	Исключение
		Результат = '00010101';
		
	КонецПопытки;
	
	Возврат Результат;
	
КонецФункции

Функция Привести_ДатаРасширенное(МассивБайтов)
	
	ПредставлениеДаты = Привести_Строка(МассивБайтов);
	Результат = '00010101';
	
	Попытка
		МинутыДаты = "00";
		СекундыДаты = "00";
		СмещениеЧасы = 0;
		СмещениеМинуты = 0;
		СмещениеЗнак = 1;
		
		ГодДаты = Сред(ПредставлениеДаты, 1, 4);
		МесяцДаты = Сред(ПредставлениеДаты, 5, 2);
		ДеньДаты = Сред(ПредставлениеДаты, 7, 2);
		ЧасДаты = Сред(ПредставлениеДаты, 9, 2);
		
		Переключатель = Сред(ПредставлениеДаты, 11, 1);
		Если Переключатель = "." Тогда
			Переключатель = "Z";
		Иначе
			МинутыДаты = Сред(ПредставлениеДаты, 11, 2);
			Переключатель = Сред(ПредставлениеДаты, 13, 1);
		КонецЕсли;
		
		Если Переключатель = "." Тогда
			Переключатель = "Z";
		ИначеЕсли Переключатель <> "Z" Тогда
			СекундыДаты = Сред(ПредставлениеДаты, 13, 2);
			Переключатель = Сред(ПредставлениеДаты, 15, 1);
		КонецЕсли;
		
		Если Переключатель = "." Тогда
			Переключатель = "Z";
		ИначеЕсли Переключатель = "-" Тогда
			СмещениеЧасы = Число(Сред(ПредставлениеДаты, 16, 2));
			СмещениеМинуты = Число(Сред(ПредставлениеДаты, 18, 2));
			СмещениеЗнак = -1;
		ИначеЕсли Переключатель = "+" Тогда
			СмещениеЧасы = Число(Сред(ПредставлениеДаты, 16, 2));
			СмещениеМинуты = Число(Сред(ПредставлениеДаты, 18, 2));
		КонецЕсли;
		
		Если СмещениеЧасы <> 0 Тогда
			ДеньДаты = Строка(Число(ЧасДаты) + СмещениеЗнак * СмещениеЧасы);
		КонецЕсли;

		Если СмещениеМинуты <> 0 Тогда
			ДеньДаты = Строка(Число(МинутыДаты) + СмещениеЗнак * СмещениеМинуты);
		КонецЕсли;
		
		Результат = Дата(ГодДаты + МесяцДаты + ДеньДаты + ЧасДаты + МинутыДаты + СекундыДаты);
		
	Исключение
		Результат = '00010101';
		
	КонецПопытки;
	
	Возврат Результат;
	
КонецФункции

Функция Привести_СтрокаUTF(ПотокБайтов)
	
	Результат = ПолучитьСтрокуИзБуфераДвоичныхДанных(ПотокБайтов, "UTF-8");
	
	Возврат Результат;
	
КонецФункции

Функция Привести_МассивHEX(ОпределениеASN, МассивБайтов, ВключитьРазделители = Истина)
	
	Результат = "";
	
	Если ОпределениеASN.ИсключитьСодержимое Тогда
		Возврат Результат;
	КонецЕсли;
	
	Результат = ПолучитьHexСтрокуИзБуфераДвоичныхДанных(МассивБайтов);
	Если НЕ ВключитьРазделители Тогда
		Результат = СтрЗаменить(Результат, " ", "");
	КонецЕсли;	
	
	Возврат Результат;
	
КонецФункции

Функция Привести_МассивБитов(МассивБайтов)
	
	Результат = "";
	
	ВсегоБайт = МассивБайтов.Размер;
	Если ВсегоБайт < 2 Тогда
		Возврат Результат;
	КонецЕсли;
	
	ЧислоБитов = МассивБайтов[0];
	
	Для Счетчик = 2 По ВсегоБайт Цикл
		РазмерОбхода = 7;
		Если ВсегоБайт - Счетчик = 0 Тогда
			РазмерОбхода = РазмерОбхода - ЧислоБитов;
		КонецЕсли;
		ЗначениеБайта = МассивБайтов[Счетчик - 1];
		БитовыйСдвиг = 128;
		Для СчетчикБитов = 0 По РазмерОбхода Цикл
			Если ПобитовоеИ(ЗначениеБайта, БитовыйСдвиг) > 0 Тогда
				Результат = Результат + "1";
			Иначе
				Результат = Результат + "0";
			КонецЕсли;
			БитовыйСдвиг = БитовыйСдвиг / 2;
		КонецЦикла;
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

Функция Привести_Идентификатор(МассивБайтов)
	
	Результат 		= "";
	Значение		= 0;
	МассивЗначений	= Новый Массив;
	Количество		= МассивБайтов.Размер;
	
	Для Счетчик = 1 По Количество Цикл
		ТекущийБайт = МассивБайтов[Счетчик - 1];
		Если Значение = 0 И ТекущийБайт = 128 Тогда
			ВызватьИсключение НСтр("ru = 'ASN.1 syntax error'");
		КонецЕсли;
		Значение = ПобитовоеИли(ПобитовыйСдвигВлево(Значение, 7), ПобитовоеИ(ТекущийБайт, 127));
		Если ПобитовоеИ(ТекущийБайт, 128) = 0 Тогда
			МассивЗначений.Добавить(Значение);
			Значение = 0;
		КонецЕсли;	
	КонецЦикла;
	
	Если МассивЗначений.Количество() = 0 ИЛИ МассивЗначений[0] > 1599 Тогда
		ВызватьИсключение НСтр("ru = 'ASN.1 syntax error'");
	КонецЕсли;
	
	ПервоеЗначение = МассивЗначений[0];
	МассивЗначений.Удалить(0);
	МассивЗначений.Вставить(0, ПервоеЗначение % 40);
	МассивЗначений.Вставить(0, Цел(ПервоеЗначение / 40));
	
	Для Счетчик = 1 По МассивЗначений.Количество() Цикл
		МассивЗначений[Счетчик - 1] = Формат(МассивЗначений[Счетчик - 1], "ЧН=; ЧГ=0");
	КонецЦикла;
	
	Результат = СтрСоединить(МассивЗначений, ".");
	
	Возврат Результат;
	
КонецФункции

Функция Привести_ЧислоHEX(Знач ТекущееЧисло, ВключитьРазделители = Ложь)
	
	Результат = "";
	
	Возможные = ОпределитьТип_HEX();
	
	Пока ТекущееЧисло > 0 Цикл
		ДобавитьЧисло = ТекущееЧисло % 256;
		ТекущееЧисло = Цел(ТекущееЧисло / 256);
		Результат = ?(ПустаяСтрока(Результат) ИЛИ НЕ ВключитьРазделители, "", " ") + Возможные[Цел(ДобавитьЧисло / 16)] + Возможные[ДобавитьЧисло % 16] + Результат;
	КонецЦикла;	
	
	Возврат Результат;
	
КонецФункции

Функция Привести_СтрокаБитов(ОпределениеASN, МассивБайтов)
	
	Результат = "";
	
	Если ОпределениеASN.ИсключитьСодержимое Тогда
		Результат = "";
	Иначе	
		Результат = Привести_МассивБитов(МассивБайтов);
	КонецЕсли;	

	Возврат Результат;
	
КонецФункции

Функция Привести_СтрокаБайтов(ОпределениеASN, МассивБайтов, СквознойСчетчик = 0)
	
	Удалось = Истина;
	
	Если ОпределениеASN.ОграничениеDER Тогда
		Удалось = Ложь;
		
	Иначе
		Попытка
			Результат = НоваяСтрока("OCTETSTRING", Новый Массив);
			ПрочитатьОчереднуюПорцию(ОпределениеASN, МассивБайтов, 1, Результат, Ложь, СквознойСчетчик);
		Исключение
			Удалось = Ложь;
			
		КонецПопытки;
		
	КонецЕсли;
	
	Если НЕ Удалось Тогда
		Если ОпределениеASN.ИсключитьСодержимое Тогда
			Результат = "";
		Иначе	
			Результат = Привести_МассивHEX(ОпределениеASN, МассивБайтов);
		КонецЕсли;	
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

Функция ПолучитьТекущееЗначение(ОпределениеASN, ПотокБайтов, ТекущийСчетчик, Длина, Тег, ПустыеБайты = Ложь, СквознойСчетчик = 0)
	
	Результат		= "";
	НовыйПоток	 	= ПолучитьСледующиеБайты(ОпределениеASN, ПотокБайтов, ТекущийСчетчик, Длина, ПустыеБайты);
	ОпределениеТипа = ОпределениеASN.Теги[Тег.Блок];
	
	Если Тег.Класс = 128 Тогда
		Значение = Привести_МассивHEX(ОпределениеASN, НовыйПоток);
	ИначеЕсли Тег.Класс = 0 И ОпределениеТипа = "BOOLEAN" Тогда
		Значение = Привести_Булево(НовыйПоток);
	ИначеЕсли ОпределениеТипа = "INTEGER" ИЛИ ОпределениеТипа = "ENUMERATED" Тогда
		Значение = Привести_Целое(НовыйПоток);
	ИначеЕсли ОпределениеТипа = "OCTETSTRING" Тогда
		Значение = Привести_СтрокаБайтов(ОпределениеASN, НовыйПоток, СквознойСчетчик);
	ИначеЕсли ОпределениеТипа = "BITSTRING" Тогда
		Значение = Привести_СтрокаБитов(ОпределениеASN, НовыйПоток);
	ИначеЕсли ОпределениеТипа = "NULL" Тогда
		Значение = Привести_Пустое(НовыйПоток);
	ИначеЕсли ОпределениеТипа = "OBJECT" Тогда
		Значение = Привести_Идентификатор(НовыйПоток);
	ИначеЕсли ОпределениеТипа = "PRINTABLESTRING"
		ИЛИ ОпределениеТипа = "IA5STRING"
		ИЛИ ОпределениеТипа = "UNICODE" Тогда
		Значение = Привести_Строка(НовыйПоток);
	ИначеЕсли ОпределениеТипа = "UTCTIME" Тогда
		Значение = Привести_Дата(НовыйПоток);
	ИначеЕсли ОпределениеТипа = "GENERALIZEDTIME" Тогда
		Значение = Привести_ДатаРасширенное(НовыйПоток);
	ИначеЕсли ОпределениеТипа = "UTF8" Тогда
		Значение = Привести_СтрокаUTF(НовыйПоток);
	ИначеЕсли ОпределениеТипа = "NUMERICSTRING" Тогда
		Значение = Привести_Строка(НовыйПоток);
	Иначе
		Значение = Привести_МассивHEX(ОпределениеASN, НовыйПоток);
	КонецЕсли;
	
	Результат = Значение;
	
	Возврат Результат;
	
КонецФункции

Функция АдресДанныхПотока(Счетчик = Неопределено, КоличествоБайт = Неопределено, ДлинаЗаголовка = Неопределено)
	
	РазделительПолей = ":";
	СоставАдреса = Новый Массив;
	СоставАдреса.Добавить(0);
	СоставАдреса.Добавить(0);
	СоставАдреса.Добавить(0);
	
	Если ЗначениеЗаполнено(Счетчик) Тогда
		СоставАдреса[0] = Формат(Счетчик, "ЧГ=");
	КонецЕсли;
	
	Если ЗначениеЗаполнено(КоличествоБайт) Тогда
		СоставАдреса[1] = Формат(КоличествоБайт, "ЧГ=");
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ДлинаЗаголовка) Тогда
		СоставАдреса[2] = Формат(ДлинаЗаголовка, "ЧГ=");
	КонецЕсли;
	
	Результат = СтрСоединить(СоставАдреса, РазделительПолей);
	
	Возврат Результат;
	
КонецФункции

Функция НоваяСтрока(ТипТега, Значение, ПотокБайтов = "")
	
	Результат = Новый Структура("Тип, Значение, Данные", ТипТега, Значение, ПотокБайтов);
	
	Возврат Результат;
	
КонецФункции

Функция НоваяВетка(ОпределениеASN, Родитель, ТипТега, Значение)
	
	ЗначениеВетки = Родитель.Значение;
	ДобавилиСтроку = НоваяСтрока(ТипТега, Значение);
	
	Если ТипЗнч(ЗначениеВетки) = Тип("Массив") Тогда
		ЗначениеВетки.Добавить(ДобавилиСтроку);
	ИначеЕсли ТипЗнч(ЗначениеВетки) = Тип("Структура") Тогда
		Количество = ЗначениеВетки.Количество();
		ЗначениеВетки.Вставить("Ветка" + Формат(Количество + 1, "ЧГ="), ДобавилиСтроку);
	КонецЕсли;	
	
	Возврат ДобавилиСтроку;
	
КонецФункции

Процедура ПрочитатьСоставнойТип(ОпределениеASN, ПотокБайтов, ТекущийСчетчик, НоваяСтрока, ТекущаяДлина, СквознойСчетчик = 0, ДлинаЗаголовкаРодителя = 0)
	
	Длина = ТекущаяДлина;
	Если ТекущаяДлина = -1 Тогда
		Длина = 0;
	КонецЕсли;
	
	Если ТекущаяДлина = -1 Тогда
		ПрочитатьОчереднуюПорцию(ОпределениеASN, ПотокБайтов, ТекущийСчетчик, НоваяСтрока, , СквознойСчетчик, ДлинаЗаголовкаРодителя);
	Иначе
		НовыйПоток = ПолучитьСледующиеБайты(ОпределениеASN, ПотокБайтов, ТекущийСчетчик, Длина);
		ПрочитатьОчереднуюПорцию(ОпределениеASN, НовыйПоток, 1, НоваяСтрока, , СквознойСчетчик, ДлинаЗаголовкаРодителя);
	КонецЕсли;
	
КонецПроцедуры

// пытаемся разобрать двоичный образ тега
Функция ПрочитатьОчереднуюПорцию(ОпределениеASN, ПотокБайтов, ТекущийСчетчик, ДеревоСтруктуры, ДобавлятьЭлемент = Истина, СквознойСчетчик = 0, ДлинаЗаголовкаРодителя = 0)
	
	Результат = Неопределено;
	
	Пока ПотокБайтов.Размер >= ТекущийСчетчик Цикл
		
		ЗаполнитьВетку	= Неопределено;
		
		НачалоСчетчика	= ТекущийСчетчик;
		ЗначениеТега	= ПолучитьТекущийТег(ОпределениеASN, ПотокБайтов, ТекущийСчетчик);
		ТекущаяДлина	= ПолучитьТекущуюДлину(ОпределениеASN, ПотокБайтов, ТекущийСчетчик);
		Длина			= ТекущаяДлина;
		ПустыеБайты		= Ложь;
		Если ТекущаяДлина = -1 Тогда
			Длина = 0;
			ПустыеБайты = Истина;
		КонецЕсли;
		ДлинаЗаголовка = ТекущийСчетчик - НачалоСчетчика;
		НачалоСчетчика = ТекущийСчетчик;
		ТипТега = ПолучитьТипТега(ОпределениеASN, ЗначениеТега);
		
		Если ПолучитьТип(ОпределениеASN, ЗначениеТега) = "Primitive" Тогда
			Значение = ПолучитьТекущееЗначение(ОпределениеASN, ПотокБайтов, ТекущийСчетчик, Длина, ЗначениеТега, ПустыеБайты, СквознойСчетчик + ТекущийСчетчик - 1);
			
			Если ДобавлятьЭлемент Тогда
				НоваяСтрока = НоваяВетка(ОпределениеASN, ДеревоСтруктуры, ТипТега, Значение);
				ЗаполнитьВетку = НоваяСтрока;
			Иначе
				ДеревоСтруктуры.Тип = ТипТега;
				ДеревоСтруктуры.Значение = Значение;
				ЗаполнитьВетку = ДеревоСтруктуры;
			КонецЕсли;	
			
		ИначеЕсли ПолучитьТип(ОпределениеASN, ЗначениеТега) = "Constructed" Тогда
			НовыйЭлемент = Новый Массив();
			НоваяСтрока = НоваяВетка(ОпределениеASN, ДеревоСтруктуры, ТипТега, НовыйЭлемент);
			ПрочитатьСоставнойТип(ОпределениеASN, ПотокБайтов, ТекущийСчетчик, НоваяСтрока, ТекущаяДлина, СквознойСчетчик + ТекущийСчетчик - 1, ДлинаЗаголовка);
			
		КонецЕсли;
		
		ОбщаяДлина = Длина;
		Если ТекущаяДлина = -1 Тогда
			ОбщаяДлина = ОбщаяДлина + 2;
		КонецЕсли;
		
		Если ЗаполнитьВетку <> Неопределено Тогда
			ЗаполнитьВетку.Данные = АдресДанныхПотока(СквознойСчетчик + НачалоСчетчика - 1, ОбщаяДлина, ДлинаЗаголовка);
		КонецЕсли;
		
		Если ТекущаяДлина = -1 Тогда
			ТекущийСчетчик = ТекущийСчетчик + 2;
		КонецЕсли;
		
	КонецЦикла;	
	
	ДеревоСтруктуры.Данные = АдресДанныхПотока(СквознойСчетчик, ПотокБайтов.Размер, ДлинаЗаголовкаРодителя);
	
	Возврат Результат;
	
КонецФункции

#КонецОбласти

#Область ЗашифрованныеДанные

Функция ЗаполнитьДанныеОбъектов(МассивВеток, Результат = Неопределено)
	
	Если Результат = Неопределено Тогда
		Результат = Новый Соответствие;
	КонецЕсли;
	
	Идентификатор = "";
	Значение = "";
	Для каждого СтрокаВетки Из МассивВеток Цикл
		Если ТипЗнч(СтрокаВетки.Значение) = Тип("Массив") Тогда
			ЗаполнитьДанныеОбъектов(СтрокаВетки.Значение, Результат);
		ИначеЕсли СтрокаВетки.Тип = "OBJECT" Тогда
			Идентификатор = СтрокаВетки.Значение;
		ИначеЕсли ЗначениеЗаполнено(Идентификатор) Тогда
			Значение = СтрокаВетки.Значение;
		КонецЕсли;	
	КонецЦикла;
	
	Если ЗначениеЗаполнено(Идентификатор) И ЗначениеЗаполнено(Значение) Тогда
		Результат.Вставить(Идентификатор, Значение);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции	

Функция ПолучитьПредставлениеОбъектов(ДанныеОбъектов, СтруктураЗаполнения = Неопределено, ФильтрПолей = "", КлючевоеПоле = "Представление")
	
	Результат = Новый Соответствие;
	
 	Тип_Объекты = ОпределитьТип_OID("", ФильтрПолей);
	Для каждого СтрокаОбъекта Из ДанныеОбъектов Цикл
		Значение = Тип_Объекты[СтрокаОбъекта.Ключ];
		Если ЗначениеЗаполнено(Значение) Тогда
			Результат.Вставить(Значение[КлючевоеПоле], СтрокаОбъекта.Значение);
		КонецЕсли;
		Если ЗначениеЗаполнено(Значение) 
			И СтруктураЗаполнения <> Неопределено 
			И СтруктураЗаполнения.Свойство(Значение[КлючевоеПоле]) Тогда
			СтруктураЗаполнения.Вставить(Значение[КлючевоеПоле], СтрокаОбъекта.Значение);
		КонецЕсли;
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

#КонецОбласти

#Область Прочее

// Получает следующий элемент в коллекции, относительно текущего
//
// Параметры:
//  Родитель - Структура - должно быть поле "Значение" с типом Массив
//  ТекущийЭлемент - Структура - элемент массива из Родитель
//
// Возвращаемое значение:
//  - Неопределено
//  - Структура
//	
Функция ПолучитьСледующийЭлемент(Родитель, ТекущийЭлемент)
	
	Результат = Неопределено;
	ПредыдущийЭлемент = Неопределено;
	
	Для Каждого СтрокаЭлемента Из Родитель.Значение Цикл
		
		Если СтрокаЭлемента = ТекущийЭлемент Тогда
			ПредыдущийЭлемент = ТекущийЭлемент;
			
		ИначеЕсли ПредыдущийЭлемент <> Неопределено Тогда
			Результат = СтрокаЭлемента;
			Прервать;
			
		КонецЕсли;
		
	КонецЦикла;	
	
	Возврат Результат;
	
КонецФункции

// Рекурсивно ищет в текущей ветке вложенную ветку с указанным значением OID
//
// Параметры:
//  СтруктураФайла 	- см. ЧтениеСтруктурыASNФайла
//   Идентификатор 	- Строка - OID значение
//  Уровень 		- Число - служебное
//
// Возвращаемое значение:
//  Структура:
//    * Уровень 	- Число - уровень где находиться найденная ветка
//    * Ветка 		- Структура:
//    * Следующий	- Структура:
//    	
//    	
//
Функция НайтиOID(СтруктураФайла, Идентификатор, Уровень = 0)
	
	Результат = Неопределено;
	НашлиЭлемент = Неопределено;
	СледующийЭлемент = Неопределено;
	КоличествоЭлементов = СтруктураФайла.Значение.Количество();
	
	Для каждого ЭлементФайла Из СтруктураФайла.Значение Цикл
		
		Если НашлиЭлемент <> Неопределено Тогда
			СледующийЭлемент = ЭлементФайла; 
			Прервать;
		ИначеЕсли ТипЗнч(ЭлементФайла.Значение) = Тип("Массив") Тогда
			РезультатПоиска = НайтиOID(ЭлементФайла, Идентификатор, Уровень + 1);
			Если РезультатПоиска <> Неопределено Тогда
				Если РезультатПоиска.Свойство("Уровень") Тогда
					Результат = РезультатПоиска;
				Иначе
					НашлиЭлемент = РезультатПоиска;
				КонецЕсли;	
			КонецЕсли;	
		ИначеЕсли ЭлементФайла.Значение = Идентификатор И ЭлементФайла.Тип = "OBJECT" Тогда
			Если КоличествоЭлементов = 1 Тогда
				Результат = ЭлементФайла;
			Иначе	
				НашлиЭлемент = ЭлементФайла;
			КонецЕсли;	
		КонецЕсли;
		
		Если Результат <> Неопределено Тогда
			Прервать;
		КонецЕсли;
		
	КонецЦикла;	
	
	Если НашлиЭлемент <> Неопределено Тогда
		Результат = Новый Структура("Ветка, Следующий, Уровень", СтруктураФайла, СледующийЭлемент, Уровень);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Рекурсивно ищет в текущей ветке вложенную ветку с указанным значением OID
//
// Параметры:
//  СтруктураФайла 	- см. ЧтениеСтруктурыASNФайла
//  Уровень			- Число - служебное
//
// Возвращаемое значение:
//  Структура:
//    * Уровень - Число - уровень где находиться найденная ветка
//    * Ветка - Структура
//
Функция НайтиПоТипуУзла(СтруктураФайла, ТипУзла, Уровень = 0)
	
	Результат = Неопределено;
	
	Для каждого ЭлементФайла Из СтруктураФайла.Значение Цикл
		Если ТипЗнч(ЭлементФайла.Значение) = Тип("Массив") Тогда
			Результат = НайтиПоТипуУзла(ЭлементФайла, ТипУзла, Уровень + 1);
		ИначеЕсли ЭлементФайла.Тип = ТипУзла И ЗначениеЗаполнено(ЭлементФайла.Значение) Тогда
			Результат = Новый Структура("Ветка, Уровень", СтруктураФайла, Уровень);
		КонецЕсли;
		Если Результат <> Неопределено Тогда
			Прервать;
		КонецЕсли;	
	КонецЦикла;	
	
	Возврат Результат;
	
КонецФункции

// функция-обертка для упрощения вызова
//
Функция СвойствоСтруктуры(ДанныеСтруктуры, ИскомоеЗначение, ЗначениеПоУмолчанию = Неопределено)
	
	Возврат СервисКриптографииDSSКлиентСервер.ПолучитьПолеСтруктуры(ДанныеСтруктуры, ИскомоеЗначение, ЗначениеПоУмолчанию);
	
КонецФункции

// Предусмотрена для анализа отладочной информации
Процедура ОтработатьВозникновениеОшибки(ОпределениеASN, ТекущаяОшибка)
	
	Если ОпределениеASN.ОтображатьОшибки Тогда
		ОбъектСообщения = Новый СообщениеПользователю();
		ОбъектСообщения.Текст = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ТекущаяОшибка); 
		ОбъектСообщения.Сообщить();
	КонецЕсли;	
	
КонецПроцедуры

#КонецОбласти

#КонецОбласти
